#include "imx307.h"
#include "ar_clk.h"

static void sensor_power_on(sensor_lib_handle_t sensor_lib)
{
    int status_reset, status_pwdn;
    cam_gpio_t gpio_reset;
    cam_gpio_t gpio_pow;

    char *name=get_sensor_lib_name(sensor_lib);
    status_reset=get_gpio_cfg(name,"reset-gpio",&gpio_reset);
    if(status_reset)
    {
        ar_info("fail to get reset pin %d",status_reset);
    }
    status_pwdn=get_gpio_cfg(name,"power-gpio",&gpio_pow);
    if(status_pwdn)
    {
        ar_info("fail to get pwdn pin %d",status_pwdn);
    }

    double clk = 37.125;
    // ar_clk_set(AR_CLK_MOD_AU_PLL_FOR_SENSOR, (int)clk);
    ar_clk_set_i2s_clock_for_sensor_double(clk);
    
    if(!status_reset) pin_share_config(gpio_reset.pad,gpio_reset.func);
    if(!status_pwdn) pin_share_config(gpio_pow.pad,gpio_pow.func);

    if(!status_reset) sirius_gpio_set_direction(gpio_reset.group,gpio_reset.port, gpio_reset.num, gpio_reset.in_out);
    if(!status_pwdn) sirius_gpio_set_direction(gpio_pow.group,gpio_pow.port, gpio_pow.num, gpio_pow.in_out);

    if(!status_reset) sirius_gpio_set_value(gpio_reset.group,gpio_reset.port, gpio_reset.num, GPIO_IS_HIGH);
    if(!status_pwdn && !status_reset) ar_delay(100);
    if(!status_pwdn) sirius_gpio_set_value(gpio_pow.group,gpio_pow.port, gpio_pow.num, GPIO_IS_HIGH);
	ar_delay(100);
    return;
}

static void sensor_power_off(sensor_lib_handle_t sensor_lib)
{
    int status_reset, status_pwdn;

    cam_gpio_t gpio_reset;
    cam_gpio_t gpio_pow;

    char *name=get_sensor_lib_name(sensor_lib);

    status_reset=get_gpio_cfg(name,"reset-gpio",&gpio_reset);
    if(status_reset)
    {
        ar_info("fail to get reset pin %d",status_reset);
    }
    status_pwdn=get_gpio_cfg(name,"power-gpio",&gpio_pow);
    if(status_pwdn)
    {
        ar_info("fail to get pwdn pin %d",status_pwdn);
    }

    if(!status_pwdn) sirius_gpio_set_value(gpio_pow.group,gpio_pow.port, gpio_pow.num, GPIO_IS_LOW);
    if(!status_pwdn && !status_reset) ar_delay(5);
    if(!status_reset) sirius_gpio_set_value(gpio_reset.group,gpio_reset.port, gpio_reset.num, GPIO_IS_LOW);

    return;
}

static  int sensor_set_led(sensor_lib_handle_t sensor_lib,int led_current)
{
    ar_always("led_current=%d",led_current);
    return 0;
}
static int sensor_set_ir_cutter(sensor_lib_handle_t sensor_lib,int enable)
{
    ar_always("enable=%d",enable);
    return 0;
}
static int  set_real_gain(sensor_lib_handle_t sensor_lib, float gain_val) {
  uint8_t gainlow=0;
  uint8_t gainhigh=0;
  uint32_t register_gain=0;
  uint32_t register_val = 0;
  int status=0;
  static int dcg_triger=0;

  if( gain_val < 1.0)
      gain_val = 1.0;
  if(gain_val > 4096.0)
      gain_val = 4096.0;
#ifdef SENSOR_HCG_MODE_ENABLE

  if(gain_val >= 64 && !dcg_triger)
  {
	  dcg_triger=1;
  }

  if(gain_val< 32 && dcg_triger)
  {
	  dcg_triger=0;
  }
  //max analog gain
  if(sensor_read_16_8(sensor_lib, 0x3009, &register_val)==0)
  {
      if(dcg_triger)
      {
         gain_val = gain_val / 2;
         register_val |= (1 << 4);
         sensor_write_16_8(sensor_lib, 0x3009, register_val);
      }else{
         register_val &= ~(1 << 4);
         sensor_write_16_8(sensor_lib, 0x3009, register_val);
      }
  }else
  {
      ar_err("i2c read err, not to wrtie hcg reg, but need change gain  value according dcg mode");
      if(dcg_triger)
      {
         gain_val = gain_val / 2;
      }else{
         gain_val=gain_val;
      }
  }
#endif

  register_gain=(uint32_t)(20*log10(gain_val)*(10/3.0));
  sensor_write_16_8(sensor_lib,0x3014, register_gain);
  return 0;
}

static int  set_exp(sensor_lib_handle_t sensor_lib, uint32_t exp_val) {

  uint8_t explow,expmid,exphigh;
  sensor_res_setting_t *res=get_current_res_setting(sensor_lib);
  uint32_t frame_length_line=get_new_frame_length(sensor_lib);
  uint8_t frame_length_line_low;
  uint8_t frame_length_line_mid;
  uint8_t frame_length_line_high;
  uint32_t interage_line=0;

  if(exp_val>0xfffff)
    exp_val=0xfffff;

  if(exp_val>=frame_length_line)
  {
	  frame_length_line=exp_val+4;//set 16 as exposure margin
  }
  interage_line=frame_length_line-exp_val-1;

  if(interage_line<=4)
    interage_line=4;

  exphigh = (uint8_t) ((0x30000&interage_line)>>16);
  expmid  = (uint8_t) ((0xff00&interage_line)>>8);
  explow  = (uint8_t) (0xff&interage_line);

  frame_length_line_low=(uint8_t)(frame_length_line&0xff);
  frame_length_line_mid=(uint8_t)((frame_length_line&0xff00)>>8);
  frame_length_line_high=(uint8_t)((frame_length_line&0x30000)>>16);


  sensor_write_16_8(sensor_lib,0x301a, frame_length_line_high);
  sensor_write_16_8(sensor_lib,0x3019, frame_length_line_mid);
  sensor_write_16_8(sensor_lib,0x3018, frame_length_line_low);


  sensor_write_16_8(sensor_lib,0x3022, exphigh);
  sensor_write_16_8(sensor_lib,0x3021, expmid);
  sensor_write_16_8(sensor_lib,0x3020, explow);
  return CAM_ERROR_SUCCUESS;
}

static int  set_real_gain_hdr(sensor_lib_handle_t sensor_lib, float gain_val,float *gain_ration) {

  uint32_t register_gain=0;
  float    long_gain=0;
  int status=0;
  float ration=gain_ration[0];

  uint32_t register_val=0;;
  if( gain_val < 1.0)
      gain_val = 1.0;
  if(gain_val > 4096.0)
      gain_val = 4096.0;

  long_gain=gain_val;
  gain_val=gain_val/ration;

  //ar_printf("short %d long %d ration=%d \n",float2int(gain_val),float2int(long_gain),float2int(ration));

  register_gain=(uint32_t)(20*log10(long_gain)*(10/3.0));
  sensor_write_16_8(sensor_lib,0x3014, register_gain);
  //register_gain=(uint32_t)(20*log10(gain_val)*(10/3.0));
  //sensor_write_16_8(sensor_lib,0x3014, register_gain);
  register_gain=(uint32_t)(20*log10(gain_val)*(10/3.0));
  sensor_write_16_8(sensor_lib,0x30f2, register_gain);

  if(sensor_read_16_8(sensor_lib, 0x3009, &register_val)==0)
  {
      register_val &= ~(1 << 4);
      sensor_write_16_8(sensor_lib, 0x3009, register_val);
  }

  return status;
}

static int  set_exp_hdr(sensor_lib_handle_t sensor_lib, uint32_t exp_val,float *time_ration)
{
  uint8_t explow,expmid,exphigh;
  sensor_res_setting_t *res=get_current_res_setting(sensor_lib);
  uint32_t frame_length_line=res->frame_length;
  uint32_t frame_length_line_short=res->frame_length_short;
  uint8_t frame_length_line_low;
  uint8_t frame_length_line_mid;
  uint8_t frame_length_line_high;
  uint32_t  interage_line=0;
  uint32_t  interage_line_long=0;
  uint32_t exp_time_long=0;
  float ration=time_ration[0];

  if(exp_val>=frame_length_line)
  {
      exp_val=frame_length_line;
  }
  exp_time_long=exp_val;
  exp_val=exp_val/ration;

  if(exp_val>=frame_length_line_short)
  {
     exp_val=frame_length_line_short;
  }

  ar_debug("short %d long %d time_ration=%d\n",exp_val,exp_time_long,float2int(ration));

  if(frame_length_line>=(exp_time_long+1))
  {
     interage_line_long=frame_length_line-exp_time_long-1;
  }else
  {
     interage_line_long=0;
  }

  if(interage_line_long>(frame_length_line-2))
  {
     interage_line_long=(frame_length_line-2);
  }

  if(interage_line_long<(frame_length_line_short+2))
  {
     interage_line_long=(frame_length_line_short+2);
  }

  if(frame_length_line_short>=exp_val+1)
  {
     interage_line=frame_length_line_short-exp_val-1;
  }else
  {
     interage_line=0;
  }


  if(interage_line>(frame_length_line_short-2))
  {
     interage_line=(frame_length_line_short-2);
  }

  if(interage_line<2)
  {
     interage_line=2;
  }
  ar_debug("short %d long %d \n",interage_line,interage_line_long);
  frame_length_line=frame_length_line/2;

  frame_length_line_low=(uint8_t)(frame_length_line&0xff);
  frame_length_line_mid=(uint8_t)((frame_length_line&0xff00)>>8);
  frame_length_line_high=(uint8_t)((frame_length_line&0x30000)>>16);

  sensor_write_16_8(sensor_lib,0x301a, frame_length_line_high);
  sensor_write_16_8(sensor_lib,0x3019, frame_length_line_mid);
  sensor_write_16_8(sensor_lib,0x3018, frame_length_line_low);

  //write short exp
  exphigh = (uint8_t) ((0x30000&interage_line)>>16);
  expmid  = (uint8_t) ((0xff00&interage_line)>>8);
  explow  = (uint8_t) (0xff&interage_line);

  sensor_write_16_8(sensor_lib,0x3022, exphigh);
  sensor_write_16_8(sensor_lib,0x3021, expmid);
  sensor_write_16_8(sensor_lib,0x3020, explow);

  //write long exp
  exphigh = (uint8_t) ((0x30000&interage_line_long)>>16);
  expmid  = (uint8_t) ((0xff00&interage_line_long)>>8);
  explow  = (uint8_t) (0xff&interage_line_long);

  sensor_write_16_8(sensor_lib,0x3026, exphigh);
  sensor_write_16_8(sensor_lib,0x3025, expmid);
  sensor_write_16_8(sensor_lib,0x3024, explow);

  return CAM_ERROR_SUCCUESS;
}

static int sensor_set_hdr(sensor_lib_handle_t sensor_lib,int enable)
{
    ar_always("enable=%d",enable);
    uint32_t register_val=0;
    if(enable)
    {
		sensor_write_16_8(sensor_lib,0x3010,0x61);
        sensor_read_16_8(sensor_lib, 0x3009, &register_val);
        register_val &= ~(1 << 4);
        sensor_write_16_8(sensor_lib, 0x3009, register_val);

    }else
    {
        //sensor_write_16_8(sensor_lib,0x3010,0x21);
        sensor_write_16_8(sensor_lib,0x3010,0x61);
    }
    return 0;
}
static int  set_exp_hdr_off(sensor_lib_handle_t sensor_lib, uint32_t exp_val,float *time_ration) {
  uint8_t explow,expmid,exphigh;
  sensor_res_setting_t *res=get_current_res_setting(sensor_lib);
  uint32_t frame_length_line=get_new_frame_length(sensor_lib);
  uint8_t frame_length_line_low;
  uint8_t frame_length_line_mid;
  uint8_t frame_length_line_high;
  uint32_t interage_line=0;

  if(exp_val>0xfffff)
    exp_val=0xfffff;

  if(exp_val>=frame_length_line)
  {
	  frame_length_line=exp_val+4;//set 16 as exposure margin
  }
  interage_line=frame_length_line-exp_val-1;

  if(interage_line<=4)
    interage_line=4;

  exphigh = (uint8_t) ((0x30000&interage_line)>>16);
  expmid  = (uint8_t) ((0xff00&interage_line)>>8);
  explow  = (uint8_t) (0xff&interage_line);

  frame_length_line=frame_length_line/2;
  frame_length_line_low=(uint8_t)(frame_length_line&0xff);
  frame_length_line_mid=(uint8_t)((frame_length_line&0xff00)>>8);
  frame_length_line_high=(uint8_t)((frame_length_line&0x30000)>>16);

  sensor_write_16_8(sensor_lib,0x301a, frame_length_line_high);
  sensor_write_16_8(sensor_lib,0x3019, frame_length_line_mid);
  sensor_write_16_8(sensor_lib,0x3018, frame_length_line_low);
  sensor_write_16_8(sensor_lib,0x3022, exphigh);
  sensor_write_16_8(sensor_lib,0x3021, expmid);
  sensor_write_16_8(sensor_lib,0x3020, explow);
  ar_debug("frame_length_line=%d line=%d",frame_length_line,exp_val);

  return CAM_ERROR_SUCCUESS;
}

static int aec_update(sensor_lib_handle_t sensor_lib,sensor_aec_update_t *update)
{
   int ret=0;
   ar_func_enter();
   if(!sensor_lib||!update)
   {
     ar_err("input pra error sensor_device=0x%p",sensor_lib);
     return CAM_ERROR_INPUT_NULL_POINT;
   }
    ar_debug("gain=%d line=%d",float2int(update->gain),update->line_count);
    //sensor_group_on(sensor_lib);
	sensor_res_setting_t *current_setting=get_current_res_setting(sensor_lib);
	int hdr_mode=get_hdr_mode(sensor_lib);
	if(current_setting->sensor_hdr)
	{
		if(hdr_mode==SENSOR_HDR_ON)
		{
			set_real_gain_hdr(sensor_lib,update->gain,update->exp_ration_gain);
			set_exp_hdr(sensor_lib,update->line_count,update->exp_ration_time);
		}else
		{
			set_real_gain(sensor_lib,update->gain);
			set_exp_hdr_off(sensor_lib,update->line_count,update->exp_ration_time);
		}

	}else{
       set_real_gain(sensor_lib,update->gain);
       set_exp(sensor_lib,update->line_count);
	}
	//sensor_group_off(sensor_lib);
	return 0;
}

static sensor_slave_info_t slave_info=
{
	/*sensor_slave_info_t *slave_infor*/
	g_read_id_setting,
	/*sensor_id[4]*/
	{0xb2,0x0c,0,0},
	/*id_count*/
	2,
	/*i2c_component_index*/
	1,
	/*slave_address_7bit*/
	0x34>>1,
	/*actuator*/
	{},
	/*eeprom*/
	{},
	/*flash*/
	{},
	/*slave_sensor*/
	{},
};


static sensor_out_infor_t g_sensor_out_info=
{
    /*out_format*/
    SENSOR_BAYER,
    /*link_mod*/
    LINK_MODE_MIPI_CSI,
    /*bit_width*/
    SENSOR_BIT_12_BIT,
    /*bayer format when out_format==SENSOR_BAYER*/
    BAYER_RGGB,
    /*yuv format when out_format ==SENSOR_YUV */
    YUV420,
    /*rgb format when out_format==SENSOR_RGB*/
    RGB888,
    /*mipi desc when link_mod==LINK_MODE_MIPI_CSI */
    {
       /*mipi_index,will be changed by dts config, here give a default*/
       0,
	   /*mipi_lane_cout*/
       2,
       /*channel*/
       {
             /*channel 0*/
          	{
          	    /*vc*/
				0,
				/*dt*/
				CSI_2_RAW10,
			},
			/*channel 1*/
          	{
    			/*vc*/
    			1,
    			/*dt*/
    			CSI_2_RAW8,
			},
			/*channel 2*/
          	{
    			/*vc*/
    			1,
    			/*dt*/
    			CSI_2_RAW8,
			},
		    /*channel 3*/
          	{
    			/*vc*/
    			1,
    			/*dt*/
    			CSI_2_RAW8,
			},
			/*channel 4*/
          	{
    			/*vc*/
    			1,
    			/*dt*/
    			CSI_2_RAW8,
			},
			/*channel 5*/
          	{
    			/*vc*/
    			1,
    			/*dt*/
    			CSI_2_RAW8,
			},
			/*channel 6*/
          	{
    			/*vc*/
    			1,
    			/*dt*/
    			CSI_2_RAW8,
			},
			/*channel 7*/
          	{
    			/*vc*/
    			1,
    			/*dt*/
    			CSI_2_RAW8,
			},
       },
       /*channel count*/
	   1,
	   /*settle_count*/
       0x18,
     },
     /*dvp dscription when link_mod==LINK_MODE_DVP*/
     {
        /*dvp_index*/
		0,
		/*dvp_bit_width*/
		10,
     },
     /*hmdi dscription when link_mod==LINK_MODE_HDMI*/
     {
        /*hdmi_index*/
		0,
		/*hdmi_media_type*/
		HDMI_VIDEO,
     },
     /*cam_infor */
      {
        /*mount_angle*/
        MOUNT_ANGLE_90,
        /*face*/
        CAM_FACE_BACK,
      },
};
static sensor_lib_cfg_t sensor_cfg=
{
    /*sensor_slave_info_t *slave_infor;*/
	&slave_info,
	/*sensor_out_infor_t  *out_infor;*/
	&g_sensor_out_info,
	/*power_on*/
	sensor_power_on,
	/*power_off*/
	sensor_power_off,
	/* pfn_sensor_lib_init init*/
	NULL,
	/*pfn_sensor_lib_ctl ctl;*/
	NULL,
	/*pfn_sensor_lib_deinit deinit*/
	NULL,
	/* pfn_sensor_lib_aec_update aec_update*/
	aec_update,
	/*register_setting_group_t init_setting*/
	{
        g_init_setting,
        sizeof(g_init_setting)/sizeof(register_setting_t),
	},
	/*register_setting_group_t stream_on;*/
    {
        g_stream_on,
        sizeof(g_stream_on)/sizeof(register_setting_t),
    },
    /*register_setting_group_t stream_off*/
    {
         g_stream_off,
         sizeof(g_stream_off)/sizeof(register_setting_t),
    },
    /* register_setting_group_t group_on*/
    {
         g_group_on,
         sizeof(g_group_on)/sizeof(register_setting_t),
    },
    /* register_setting_group_t group_off;*/
    {
         g_group_off,
         sizeof(g_group_off)/sizeof(register_setting_t),
    },
	/*sensor_res_setting_t *sensor_res_setting*/
    g_res_config_setting,
    sizeof(g_res_config_setting)/sizeof(sensor_res_setting_t),
    	 /*register_setting_group_t flip;*/
	{
		  /*   FLIP_OFF_MIRROR_OFF,*/
		  {
			  g_flip_off_mirro_off,
			  sizeof(g_flip_off_mirro_off)/sizeof(register_setting_t),
		  },
		  /*FLIP_OFF_MIRROR_ON*/
		  {
			  g_flip_off_mirro_on,
			  sizeof(g_flip_off_mirro_on)/sizeof(register_setting_t),
		  },
		  /*FLIP_ON_MIRROR_OFF*/
		  {
			  g_flip_on_mirro_off,
			  sizeof(g_flip_on_mirro_off)/sizeof(register_setting_t),
		  },
		  /*FLIP_ON_MIRRO_ON,*/
		  {
			  g_flip_on_mirro_on,
			  sizeof(g_flip_on_mirro_on)/sizeof(register_setting_t),
		  },
	 },
	 /*pfn_sensor_lib_set_led set_led;*/
     sensor_set_led,
     /*pfn_sensor_lib_set_ir_cutter set_ir_cutter;*/
     sensor_set_ir_cutter,
     /*pfn_sensor_lib_set_hdr set_hdr*/
     sensor_set_hdr,
};

int imx307_module_creat(char *name,module_handle_t *module,module_handle_t prarent,uint32_t index)
{
  sensor_lib_creat(name,&sensor_cfg,module,prarent,index);
  return 0;
}
int imx307_module_delete(module_handle_t *module)
{
  return sensor_lib_delete(module);
}
